﻿using System;
using System.Collections.ObjectModel;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using SessionHunter.Business;
using SessionHunter.Messages;
using SessionHunter.Model;

namespace SessionHunter.ViewModel
{

    /// <summary>
    /// Manages the references to assemblies for the application
    /// </summary>
    public class ReferencesViewModel : ViewModelBase
    {
        private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

        #region RelayCommands - Buttons
        /// <summary>
        /// To send a message to add a new reference
        /// </summary>
        public RelayCommand Command_AddReference { get; private set; }
        /// <summary>
        /// To send a message to remove an existing reference
        /// </summary>
        public RelayCommand Command_RemoveReference { get; private set; }
        /// <summary>
        /// To send a message to add all of the referenced assemblies for an external reference
        /// </summary>
        public RelayCommand Command_AddReferencedAssemblies { get; private set; }
        /// <summary>
        /// To send a message to close the window
        /// </summary>
        public RelayCommand Command_Close { get; private set; }
        #endregion

        /// <summary>
        /// Initializes a new instance of the ReferencesViewModel class.
        /// </summary>
        public ReferencesViewModel()
        {
            Command_AddReference = new RelayCommand(AddReference);
            Command_RemoveReference = new RelayCommand(RemoveReference);
            Command_Close = new RelayCommand(Close);
            Command_AddReferencedAssemblies = new RelayCommand(AddReferencedAssembliesToMissingAssemblies);
        }

        #region Missing Assemblies
        /// <summary>
        /// Gets or sets the list of missing assemblies.
        /// </summary>
        /// <value>
        /// A list of assemblies that cannot be resolved when trying to deserialize the data
        /// </value>
        public virtual ObservableCollection<string> MissingAssemblies
        {
            get
            {
                return ViewModelLocator.MainStatic.MissingAssemblies;
            }
            set
            {
                ViewModelLocator.MainStatic.MissingAssemblies = value;
                RaisePropertyChanged("MissingAssemblies");
            }
        }

        /// <summary>
        /// Gets the current reference manager.
        /// </summary>
        protected internal virtual ReferenceManager CurrentReferenceManager
        {
            get
            {
                return ViewModelLocator.MainStatic.ReferenceManager;
            }
        }

        /// <summary>
        /// This will take all of the assemblies referenced by an external reference and add them to
        /// the missing assemblies list
        /// </summary>
        public void AddReferencedAssembliesToMissingAssemblies()
        {
            var externalReference = CurrentReferences_SelectedItem;

            if (externalReference == null || !String.IsNullOrEmpty(externalReference.Error))
                return;

            var dependencies = CurrentReferenceManager.GetReferencedAssemblies(externalReference);
            if (dependencies != null && dependencies.Count > 0)
            {
                ObservableCollection<string> missingAssemblies;

                // Create a new list of assemblies if there isn't one
                if (MissingAssemblies == null)
                    missingAssemblies = new ObservableCollection<string>();
                else
                    missingAssemblies = ViewModelLocator.MainStatic.MissingAssemblies;

                foreach (var dependency in dependencies)
                {
                    if (!missingAssemblies.Contains(dependency)) // Do not add it if it already exists
                        missingAssemblies.Add(dependency);
                }

                MissingAssemblies = missingAssemblies;
            }
        }
        #endregion

        #region Current References

        /// <summary>
        /// Gets or sets the list of currently configured references to assemblies outside this project
        /// </summary>
        /// <value>
        /// The current references.
        /// </value>
        public ObservableCollection<ExternalReference> CurrentReferences
        {
            get
            {
                var references = CurrentReferenceManager.CurrentReferences;
                if (references != null)
                    return new ObservableCollection<ExternalReference>(ViewModelLocator.MainStatic.ReferenceManager.CurrentReferences);
                else
                    return null;
            }
        }

        /// <summary>
        /// Gets or sets the currently selected item in the reference list
        /// </summary>
        /// <value>
        /// The item or null
        /// </value>
        public ExternalReference CurrentReferences_SelectedItem
        {
            get;
            set;
        }


        /// <summary>
        /// Opens the dialog to add a reference
        /// </summary>
        private void AddReference()
        {
            logger.Debug("Opening dialog to add reference");

            Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.DefaultExt = ".dll"; // Default file extension
            dlg.Filter = "Assemblies|*.dll;*.exe"; // Filter files by extension

            // Show open file dialog box
            Nullable<bool> result = dlg.ShowDialog();

            // Process open file dialog box results
            if (result == true)
            {
                // Open document
                string filename = dlg.FileName;
                logger.Info("Adding reference to assembly at [{0}]", filename);
                ViewModelLocator.MainStatic.ReferenceManager.AddDynamicReference(filename);
                ViewModelLocator.MainStatic.ReferenceManager.RemoveMissingAssemblies(MissingAssemblies);
                if (MissingAssemblies != null && MissingAssemblies.Count == 0)
                    MissingAssemblies = null; // To make it consistent, make it null if we have none left

                RaisePropertyChanged("CurrentReferences"); // Tell references listbox to get the updated info, which may also have errors
                Messenger.Default.Send(new MainWindowMessage(MainWindowMessage.MessageAction.Clear_SessionList));
                Messenger.Default.Send(new MainViewModelMessage(MainViewModelMessage.MessageAction.SaveReferencesToSettings));
            }
        }

        /// <summary>
        /// Removes an existing reference from the system and the list
        /// </summary>
        private void RemoveReference()
        {
            var referenceToRemove = CurrentReferences_SelectedItem;
            if (referenceToRemove != null)
            {
                logger.Info("Removing the reference to [" + referenceToRemove.FullAssemblyName + "] at path [" + referenceToRemove.Path + "]");
                ViewModelLocator.MainStatic.ReferenceManager.RemoveDynamicReference(CurrentReferences_SelectedItem);
                CurrentReferences_SelectedItem = null; // Clear it so that we can select another one
                RaisePropertyChanged("CurrentReferences"); // Tell references listbox to get the updated info, which may also have errors
                Messenger.Default.Send(new MainViewModelMessage(MainViewModelMessage.MessageAction.SaveReferencesToSettings));
            }
        }
        #endregion


        /// <summary>
        /// Closes the current window
        /// </summary>
        private void Close()
        {
            logger.Debug("Closing ReferencesViewModel");
            Messenger.Default.Send(new MainViewModelMessage(MainViewModelMessage.MessageAction.CloseReferencesView));
        }
    }
}